﻿using System.Diagnostics;
using System.Net;
using System.Text;
using System.Text.Json;
using NSec.Cryptography;

namespace WeatherSDK;

/// <summary>
/// 和风天气API
/// </summary>
public class QWeatherProvider : BaseApiProvider, IWeatherApi
{
    private static class JwtGenerator
    {
        public static string GenerateEdDsaJwt(string privateKeyPem, string publicKeyId, string projectId)
        {
            // 处理私钥 - 移除PEM头尾和换行符
            var privateKey = privateKeyPem
                .Replace("-----BEGIN PRIVATE KEY-----", "")
                .Replace("-----END PRIVATE KEY-----", "")
                .Replace("\n", "")
                .Replace("\r", "")
                .Trim();

            Debug.WriteLine($"处理后的私钥长度: {privateKey.Length}");
            Debug.WriteLine($"私钥内容: {privateKey}");

            byte[] privateKeyBytes = Convert.FromBase64String(privateKey);
            Debug.WriteLine($"私钥字节长度: {privateKeyBytes.Length}");

            // 打印私钥的十六进制表示
            Debug.WriteLine($"私钥十六进制: {BitConverter.ToString(privateKeyBytes).Replace("-", "")}");

            // 尝试解析PKCS8结构
            if (privateKeyBytes.Length >= 2)
            {
                Debug.WriteLine($"前两个字节: {privateKeyBytes[0]:X2} {privateKeyBytes[1]:X2}");
            }

            // 使用NSec.Cryptography库的Ed25519算法
            var algorithm = SignatureAlgorithm.Ed25519;

            // 从PKCS8格式提取原始私钥
            var rawPrivateKey = ExtractRawPrivateKeyFromPkcs8(privateKeyBytes);
            if (rawPrivateKey == null)
            {
                throw new Exception("无法从PKCS8格式提取私钥");
            }

            Debug.WriteLine($"提取的原始私钥长度: {rawPrivateKey.Length}");
            Debug.WriteLine($"原始私钥十六进制: {BitConverter.ToString(rawPrivateKey).Replace("-", "")}");

            // 如果私钥数据以0x04开头，说明还包含OCTET STRING头部，需要去掉
            byte[] actualPrivateKey;
            if (rawPrivateKey[0] == 0x04)
            {
                int keyLength = rawPrivateKey[1];
                if (keyLength == 0x20) // 32字节
                {
                    actualPrivateKey = new byte[32];
                    Array.Copy(rawPrivateKey, 2, actualPrivateKey, 0, 32);
                    Debug.WriteLine($"去掉OCTET STRING头部后的私钥长度: {actualPrivateKey.Length}");
                    Debug.WriteLine($"实际私钥十六进制: {BitConverter.ToString(actualPrivateKey).Replace("-", "")}");
                }
                else
                {
                    throw new Exception($"不支持的私钥长度: {keyLength}");
                }
            }
            else
            {
                actualPrivateKey = rawPrivateKey;
            }

            // 创建Ed25519密钥
            var key = Key.Import(algorithm, actualPrivateKey, KeyBlobFormat.RawPrivateKey);

            // 生成Header和Payload
            var iat = DateTimeOffset.UtcNow.ToUnixTimeSeconds() - 30;
            var exp = iat + 900;

            var header = $@"{{""alg"":""EdDSA"",""kid"":""{publicKeyId}""}}";
            var payload = $@"{{""sub"":""{projectId}"",""iat"":{iat},""exp"":{exp}}}";

            // Base64Url编码
            var headerEncoded = Base64UrlEncode(Encoding.UTF8.GetBytes(header));
            var payloadEncoded = Base64UrlEncode(Encoding.UTF8.GetBytes(payload));
            var data = $"{headerEncoded}.{payloadEncoded}";

            // 使用Ed25519签名
            var dataBytes = Encoding.UTF8.GetBytes(data);
            var signature = algorithm.Sign(key, dataBytes);
            var signatureEncoded = Base64UrlEncode(signature);

            return $"{data}.{signatureEncoded}";
        }

        private static byte[] ExtractRawPrivateKeyFromPkcs8(byte[] pkcs8Bytes)
        {
            try
            {
                // 正确的PKCS8 Ed25519结构:
                // SEQUENCE -> INTEGER(0) -> SEQUENCE -> OBJECT IDENTIFIER(Ed25519) -> OCTET STRING -> [私钥数据]
                int offset = 0;

                // 跳过第一个SEQUENCE (0x30)
                if (pkcs8Bytes[offset] != 0x30)
                {
                    Debug.WriteLine($"第一个字节不是SEQUENCE: {pkcs8Bytes[offset]:X2}");
                    return null;
                }

                offset++; // 跳过类型
                int length = pkcs8Bytes[offset];
                Debug.WriteLine($"第一个SEQUENCE长度: {length}");
                offset++; // 跳过长度

                // 如果长度是0x81，说明长度字段是2字节
                if (length == 0x81)
                {
                    length = pkcs8Bytes[offset];
                    Debug.WriteLine($"第一个SEQUENCE实际长度: {length}");
                    offset++;
                }

                // 跳过INTEGER(0) (0x02 0x01 0x00)
                if (pkcs8Bytes[offset] != 0x02)
                {
                    Debug.WriteLine($"第二个字节不是INTEGER: {pkcs8Bytes[offset]:X2}");
                    return null;
                }

                offset++; // 跳过类型
                length = pkcs8Bytes[offset];
                Debug.WriteLine($"INTEGER长度: {length}");
                offset++; // 跳过长度
                offset += length; // 跳过值

                // 跳过第二个SEQUENCE (0x30)
                if (pkcs8Bytes[offset] != 0x30)
                {
                    Debug.WriteLine($"当前字节不是SEQUENCE: {pkcs8Bytes[offset]:X2}");
                    return null;
                }

                offset++; // 跳过类型
                length = pkcs8Bytes[offset];
                Debug.WriteLine($"第二个SEQUENCE长度: {length}");
                offset++; // 跳过长度

                // 如果长度是0x81，说明长度字段是2字节
                if (length == 0x81)
                {
                    length = pkcs8Bytes[offset];
                    Debug.WriteLine($"第二个SEQUENCE实际长度: {length}");
                    offset++;
                }

                // 跳过OBJECT IDENTIFIER (0x06)
                if (pkcs8Bytes[offset] != 0x06)
                {
                    Debug.WriteLine($"当前字节不是OBJECT IDENTIFIER: {pkcs8Bytes[offset]:X2}");
                    return null;
                }

                offset++; // 跳过类型
                length = pkcs8Bytes[offset];
                Debug.WriteLine($"OBJECT IDENTIFIER长度: {length}");
                offset++; // 跳过长度
                offset += length; // 跳过值

                // 跳过OCTET STRING (0x04)
                if (pkcs8Bytes[offset] != 0x04)
                {
                    Debug.WriteLine($"当前字节不是OCTET STRING: {pkcs8Bytes[offset]:X2}");
                    return null;
                }

                offset++; // 跳过类型
                length = pkcs8Bytes[offset];
                Debug.WriteLine($"OCTET STRING长度: {length}");
                offset++; // 跳过长度

                // 如果长度是0x81，说明长度字段是2字节
                if (length == 0x81)
                {
                    length = pkcs8Bytes[offset];
                    Debug.WriteLine($"OCTET STRING实际长度: {length}");
                    offset++;
                }

                Debug.WriteLine($"当前偏移量: {offset}, 私钥长度: {length}");

                // 现在offset指向私钥数据
                if (offset + length <= pkcs8Bytes.Length)
                {
                    var rawPrivateKey = new byte[length];
                    Array.Copy(pkcs8Bytes, offset, rawPrivateKey, 0, length);
                    Debug.WriteLine($"成功提取私钥，长度: {rawPrivateKey.Length}");
                    return rawPrivateKey;
                }
                else
                {
                    Debug.WriteLine($"偏移量超出范围: offset={offset}, length={length}, total={pkcs8Bytes.Length}");
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"PKCS8解析错误: {ex.Message}");
            }

            return null;
        }

        private static string Base64UrlEncode(byte[] input)
        {
            return Convert.ToBase64String(input)
                .Replace('+', '-')
                .Replace('/', '_')
                .TrimEnd('=');
        }
    }

    private class LookupCityResponse
    {
        public string Code { get; set; }
        public List<LocationDTO> Location { get; set; }
    }

    private class LocationDTO
    {
        /// <summary>
        /// 地区/城市名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 地区/城市ID
        /// </summary>
        public string Id { get; set; }

        /// <summary>
        /// 地区/城市所属一级行政区域
        /// </summary>
        public string Adm1 { get; set; }

        /// <summary>
        /// 地区/城市的上级行政区划名称
        /// </summary>
        public string Adm2 { get; set; }

        /// <summary>
        /// 地区/城市纬度
        /// </summary>
        public string Lat { get; set; }

        /// <summary>
        /// 地区/城市经度
        /// </summary>
        public string Lon { get; set; }
    }

    private class LiveWeatherResponse
    {
        public string Code { get; set; }
        public NowWeatherDTO Now { get; set; }
    }

    private class NowWeatherDTO
    {
        /// <summary>
        /// 数据观测时间
        /// </summary>
        public string ObsTime { get; set; }

        /// <summary>
        /// 数据观测时间
        /// </summary>
        public string Temp { get; set; }

        /// <summary>
        /// 体感温度，默认单位：摄氏度
        /// </summary>
        public string FeelsLike { get; set; }

        /// <summary>
        /// 天气状况的文字描述，包括阴晴雨雪等天气状态的描述
        /// </summary>
        public string Text { get; set; }

        /// <summary>
        /// 风向360角度
        /// </summary>
        public string Wind360 { get; set; }

        /// <summary>
        /// 风向
        /// </summary>
        public string WindDir { get; set; }

        /// <summary>
        /// 风力等级
        /// </summary>
        public string WindScale { get; set; }

        /// <summary>
        /// 风速，公里/小时
        /// </summary>
        public string WindSpeed { get; set; }

        /// <summary>
        /// 相对湿度，百分比数值
        /// </summary>
        public string Humidity { get; set; }

        /// <summary>
        /// 过去1小时降水量，默认单位：毫米
        /// </summary>
        public string Precip { get; set; }

        /// <summary>
        /// 大气压强，默认单位：百帕
        /// </summary>
        public string Pressure { get; set; }

        /// <summary>
        /// 能见度，默认单位：公里
        /// </summary>
        public string Vis { get; set; }
    }

    private class WeatherForecastResponse
    {
        public string Code { get; set; }
        public List<DailyWeatherDTO> Daily { get; set; }
    }

    private class DailyWeatherDTO
    {
        /// <summary>
        /// 预报日期
        /// </summary>
        public string FxDate { get; set; }

        /// <summary>
        /// 预报当天最高温度
        /// </summary>
        public string TempMax { get; set; }

        /// <summary>
        /// 预报当天最低温度
        /// </summary>
        public string TempMin { get; set; }

        /// <summary>
        /// 预报白天风向360角度
        /// </summary>
        public string Wind360Day { get; set; }

        /// <summary>
        /// 预报白天风向
        /// </summary>
        public string WindDirDay { get; set; }

        /// <summary>
        /// 预报白天风力等级
        /// </summary>
        public string WindScaleDay { get; set; }

        /// <summary>
        /// 预报夜间风向360角度
        /// </summary>
        public string Wind360Night { get; set; }

        /// <summary>
        /// 预报夜间当天风向
        /// </summary>
        public string WindDirNight { get; set; }

        /// <summary>
        /// 预报夜间风力等级
        /// </summary>
        public string WindScaleNight { get; set; }
    }

    private class ConfigObj
    {
        public string ProjectId { get; set; }
        public string PublicKeyId { get; set; }
        public string PrivateKey { get; set; }
    }

    private readonly HttpClient _httpClient;

    private readonly ConfigObj _configObj;

    public QWeatherProvider()
    {
        var host = ReadConfig("QWeather:Host");
        var publicKeyId = ReadConfig("QWeather:PublicKeyId");
        var projectId = ReadConfig("QWeather:ProjectId");
        var privateKey = ReadConfig("QWeather:PrivateKey");
        CheckInput(host, publicKeyId, privateKey, projectId);

        _configObj = new ConfigObj()
        {
            ProjectId = projectId,
            PublicKeyId = publicKeyId,
            PrivateKey = privateKey,
        };

        _httpClient = new HttpClient(new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip })
        {
            BaseAddress = new Uri(host),
            Timeout = TimeSpan.FromSeconds(Timeout),
        };
    }

    public QWeatherProvider(string host, string publicKeyId, string privateKey, string projectId)
    {
        CheckInput(host, publicKeyId, privateKey, projectId);

        _configObj = new ConfigObj()
        {
            ProjectId = projectId,
            PublicKeyId = publicKeyId,
            PrivateKey = privateKey,
        };

        _httpClient = new HttpClient(new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip }) { BaseAddress = new Uri(host) };
    }

    private void CheckInput(string host, string publicKeyId, string privateKey, string projectId)
    {
        if (string.IsNullOrEmpty(host))
        {
            throw new ApplicationException("未指定服务器主机地址");
        }

        if (string.IsNullOrEmpty(publicKeyId))
        {
            throw new ApplicationException("未指定接口公钥Id");
        }

        if (string.IsNullOrEmpty(projectId))
        {
            throw new ApplicationException("未指定接口项目Id");
        }

        if (string.IsNullOrEmpty(privateKey))
        {
            throw new ApplicationException("未指定接口私钥");
        }
    }

    private readonly JsonSerializerOptions _jsonSerializerOptions = new()
    {
        PropertyNameCaseInsensitive = true,
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase
    };

    public async Task<LiveWeatherDTO> GetLive(string admCode)
    {
        var cityLookupUrl = $"/geo/v2/city/lookup?location={admCode}";

        var token = JwtGenerator.GenerateEdDsaJwt(_configObj.PrivateKey, _configObj.PublicKeyId, _configObj.ProjectId);

        // 读取城市id
        var responseBody = await GetAsJson(_httpClient, cityLookupUrl, token);
        var cityResponseDto = JsonSerializer.Deserialize<LookupCityResponse>(responseBody, _jsonSerializerOptions);
        if (cityResponseDto == null || cityResponseDto.Code != "200" || cityResponseDto.Location == null || cityResponseDto.Location.Count == 0)
        {
            return null;
        }

        var location = cityResponseDto.Location[0];

        // 获取实时天气
        var weatherUrl = $"/v7/weather/now?location={location.Id}";
        responseBody = await GetAsJson(_httpClient, weatherUrl, token);
        var weatherResponseDto = JsonSerializer.Deserialize<LiveWeatherResponse>(responseBody, _jsonSerializerOptions);
        if (weatherResponseDto == null || weatherResponseDto.Code != "200" || weatherResponseDto.Now == null)
        {
            return null;
        }

        var liveWeatherDto = new LiveWeatherDTO()
        {
            Province = location.Adm1,
            City = location.Name,
            Adcode = location.Id,
            Weather = weatherResponseDto.Now.Text,
            Temperature = weatherResponseDto.Now.Temp,
            WindDirection = weatherResponseDto.Now.WindDir,
            WindPower = weatherResponseDto.Now.WindScale,
            Humidity = weatherResponseDto.Now.Humidity,
            ReportTime = Convert.ToDateTime(weatherResponseDto.Now.ObsTime)
        };

        // 获取3天内的天气预报
        var weatherForecastUrl = $"/v7/weather/3d?location={location.Id}";
        responseBody = await GetAsJson(_httpClient, weatherForecastUrl, token);
        var weatherForecastResponseDto = JsonSerializer.Deserialize<WeatherForecastResponse>(responseBody, _jsonSerializerOptions);
        if (weatherForecastResponseDto is { Code: "200", Daily.Count: > 0 })
        {
            var today = weatherForecastResponseDto.Daily[0];
            liveWeatherDto.TemperatureMin = today.TempMin;
            liveWeatherDto.TemperatureMax = today.TempMax;
        }

        return liveWeatherDto;
    }

    private async Task<string> GetAsJson(HttpClient httpClient, string url, string token)
    {
        using var request = new HttpRequestMessage(HttpMethod.Get, url);
        request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
        var response = await httpClient.SendAsync(request);
        var responseString = await response.Content.ReadAsStringAsync();
        if (response.IsSuccessStatusCode)
        {
            return responseString;
        }

        var errorResponseDto = JsonSerializer.Deserialize<ErrorResponseDTO>(responseString, _jsonSerializerOptions);
        throw new QWeatherException(errorResponseDto.Error);
    }

    private class ErrorResponseDTO
    {
        public ErrorResponseDetailDTO Error { get; set; }
    }

    public class ErrorResponseDetailDTO
    {
        public int Status { get; set; }
        public string Type { get; set; }
        public string Title { get; set; }
        public string Detail { get; set; }
    }

    internal class QWeatherException(ErrorResponseDetailDTO error) : ApplicationException(error.Detail)
    {
        public ErrorResponseDetailDTO Error { get; set; }
    }
}